#coding=utf-8
from source.util.DateTimeUtil import *
import yaml
from source.util.SystemLog import SystemLog
from source.util.AdbUtils import ADB
import traceback

from source.util.GenerateReports import GenerateReports
from source.util.Performance import make_graph
from source.util.ZipUtil import zip_dir, zip_dir_to_one
from source.util.ConfigUtil import ConfigIni
from threading import Thread
from source.util.GetCase import get_yaml_info
from datetime import datetime
import os
import time
from source.util.PixelCompareUtil import PixelCompareUtil
from source.util.LogUtil import log
from source.service.CaptureThread import CaptureThread
from source.util.ScreenShot import getscreencap
from source.util.CommonUtil import getProjectDir
import csv

import sys
reload(sys)
sys.setdefaultencoding('utf8')

class Executor(object):
    def __init__(self, driver, yaml_name, yaml_path, all_result_path, device_name,device_info,logger,caputre_card):
        self.path_yaml = yaml_path
        self.filename = str(yaml_name).split('.')[0]
        self.device = device_name
        self.all_result_path = all_result_path
        self.logger = logger
        self.driver=driver
        self.dash_page =driver
        self.data={}
        self.pic_list=[]
        self.hit_image=[] #命中图片列表
        self.runFlag=True #标记是否采集cpu
        self.adb = ADB(self.device)
        self.device_info=device_info
        self.caputre_card=caputre_card
        self.ini = ConfigIni()

    def __save_android_log(self):
        '''
        :return: 记录当前设备log
        '''
        android_log=SystemLog(self.device)
        log_file=self.all_result_path[0]+'/{}.log'.format(self.filename)
        android_log.record(log_file)
        return log_file

    def __save_error_status(self):
        error_file = self.all_result_path[3] + '/{}.yaml'.format(self.filename)
        return error_file

    @log()
    def __save_screen_file(self,filename=''):
        '''
        截图：
        1.首先调用appium自带方法，如果失败调用minicao
        2.minicap失败调用adb截图
        :return: 存储图片地址
        '''
        dir_path=self.all_result_path[0] + '/'
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        if filename=='':
            # filename=self.filename+".jpg"
            filename = str(int(time.time())) + ".jpg"
        if not filename.endswith('.jpg'):
            filename=os.path.splitext(filename)[0]+'.jpg'

        #将文件名强转为拼音
        # filename=slugify(os.path.splitext(filename)[0])+".jpg"
        # screen_file = self.all_result_path[2] + '/{}'.format(filename)
        try:
            # self.dash_page.save_screenshot(screen_file)
            #getscreencap(filename,self.all_result_path[2]+'/')
            # 采用视频检测盒截屏
            cur_work_path=os.getcwd()
            os.chdir(os.path.dirname(self.all_result_path[2]))
            self.caputre_card.takePicture('img/'+filename)
            os.chdir(cur_work_path)
        except Exception as e:
            self.logger.info('__save_screen_file:err,'+str(e))
            # self.dash_page.save_screenshot(screen_file)
        return filename

    @log()
    def __generate_test_report(self):
        '''
        生产测试报告
        :return: 测试报告路径
        '''
        os.chdir(getProjectDir())
        result = GenerateReports(os.path.dirname(self.all_result_path[0]), self.device)
        result.make()
        return self.all_result_path

    def __save_cpu_mem(self, cpu_list, mem_list,logger):
        # pic_file_path = self.all_result_path[1] + '/{}.png'.format(self.filename)
        header = ["time", "cpu_value"]
        cpu_csv = self.all_result_path[1] + '/{}_cpu.csv'.format(self.filename)
        with open(cpu_csv, 'w') as f:
            f_csv = csv.DictWriter(f, header)
            f_csv.writeheader()
            # f_csv = csv.writer(f)
            # f_csv.writerow(headers)
            f_csv.writerows(cpu_list)
            pass
        header = ["time", "mem_value"]
        mem_csv = self.all_result_path[1] + '/{}_mem.csv'.format(self.filename)
        with open(mem_csv, 'w') as f:
            # f_csv = csv.writer(f)
            f_csv = csv.DictWriter(f, header)
            f_csv.writeheader()
            f_csv.writerows(mem_list)
            pass

        pic_file_path = self.all_result_path[1] + '/{}.png'.format(self.filename)
        # make_graph(cpu_list, mem_list,pic_file_path,logger)
        return pic_file_path

    def collectCpuAndMem(self,package_name):

        while self.runFlag:

            cpu =  self.adb.get_cpu(package_name)
            mem =  self.adb.get_mem(package_name)
            collect_time=datetime.now().strftime('%H:%M:%S')
            cpu_list.append({'time':collect_time,"cpu_value":cpu})
            mem_list.append({'time':collect_time,"mem_value":mem})
            time.sleep(0.2)



    def save_result2db(self,attachment_path):
        self.data['cpu_list']=str(cpu_list)
        self.data['mem_list']=str(mem_list)
        device={'platform':'Android','version':self.adb.get_android_version(),'deviceName':self.device_info["sn"]}
        self.data['device_info']=str(device)
        self.data['pic_name'] =str(self.pic_list)
        self.data['log_name']=str(['outPut.log'])

        path=self.ini.get_ini('test_case','case')
        # print('test_case_path=',path)
        if not self.device_info['taskcode']:
            self.data['taskcode'] = '本地脚本'
            self.data["cur_exe_times"] =1
        else:
            self.data['taskcode'] = self.device_info['taskcode']
            self.data["cur_exe_times"] = self.device_info["cur_exe_times"]

        self.data['script_name'] = self.filename+".yaml"

        # FIXME 结果存入数据库
        # test_result = Result()
        # test_result.content = json.dumps(self.data)
        # # attachment_path = base_dir + '/TestResult/' + self.data['attachment_name'] + '.zip'
        # test_result.attachment_path = attachment_path
        # test_result.status = 0
        # # 将结果插入数据库
        # ResultDao.insert(test_result)
        self.runFlag=False

        pass

    @log()
    def __parse_yaml(self, global_setting,items):
        '''
        解析测试用例
        :param path_yaml: 测试用例地址
        :return:
        '''
        try:
            global is_collect

            global result

            global cost_time

            cost_time=""
            # if 'cpu_mem' in global_setting:
            #     is_collect=global_setting.get('cpu_mem')
            # else:
            #     is_collect=True
            is_collect = True  #暂时开启
            # if is_collect:
            #     Thread(target=self.collectCpuAndMem,args=(global_setting.get('package_name'),)).start()
            for item in items:
                result=self.check_step(item)
                if not result:
                    break

        except Exception as e:
            self.logger.info(str(e))
            result=False
        finally:
            #删除临时存放数据
            # self.dash_page.remove_section()
            self.logger.info('将cpu、mem数据展示成图片')
            self.__save_cpu_mem(cpu_list, mem_list,self.logger)
            is_collect = False
            self.logger.info('cpu_list:{}'.format(cpu_list))
            self.logger.info('mem_list:{}'.format(mem_list))
            return result

    @log()
    def check_step(self,item):
        self.logger.info(str(item))
        result=True
        if isinstance(item, dict):
            # FIXME 便签目前不允许嵌套
            # 检测item是不是for标签
            if 'flag' in item:
                if item['flag']=='for':
                    for_num = item['num']
                    scripts = item['scripts']
                    result=self.flag_script_exec(scripts,for_num)
                    return result
                elif item['flag']=='if':
                    result = self.exec_step(item)
                    if result:
                        scripts = item['scripts']
                        for j in range(len(scripts)): #fi标签下item长度
                            try:
                                result = self.check_step(scripts[j])
                            except Exception as e:
                                self.logger.info('步骤{}执行异常,异常信息为{}，退出'.format(str(scripts[j]),e))
                            if not result:
                                self.logger.info('步骤{}执行异常，退出'.format(str(scripts[j])))
                                break
                        return result
                    else:
                        if 'optional' in item:
                            optional = item['optional']
                            if optional:
                                # if是否可以跳过
                                return True
                else:
                    self.logger.info('执行{}代码块,功能开发中……'.format(item['flag']))
                    return False
                # if not result:
                #     return result
            else:
                result = self.exec_step(item)
            return result
        else:
            self.logger.info('Yaml file format error,the current {} ,you need dict'.format(type(dict)))
            return False

    @log()
    def flag_script_exec(self,scripts,num):
        if num<=0:
            return True
        result=False
        self.logger.info('---------当前循环第%d次------' % (num + 1))
        for j in range(len(scripts)):  # for标签下item长度
            try:
                result = self.check_step(scripts[j])
            except Exception as e:
                self.logger.info('步骤{}执行异常,异常信息为{}，退出'.format(str(scripts[j]), e))

            if not result:
                self.logger.info('步骤{}执行异常，退出'.format(str(scripts[j])))
                break
        # FIXME 获取当前应用的包名，不匹配按home键
        if not result:
            if self.adb.get_front_package_name().strip() != self.device_info['appPackage']:
                self.logger.info('前端应用包{}不正确，尝试进入首页{}'.format(self.adb.get_front_package_name().strip(),
                                                                self.device_info['appPackage']))
                self.dash_page.send_key_event(3)
                time.sleep(5)
            return result
        num=num-1
        self.logger.info("cunrrent num=",str(num))
        self.flag_script_exec(scripts,num)
        return result

    @log()
    def exec_step(self,item):
        sleep_time = self.default_test_sleep  # 默认sleep 1000毫秒
        global cost_time,spend_time,c_time
        c_time=-1  #图片创建时间
        spend_time=-1  #比对时间结果
        #cost_time ="" #像素比对结果
        if 'test_sleep' in item:
            sleep_time = int(item['test_sleep'])
            self.logger.info('Wait {} seconds'.format(sleep_time))

        test_desc = item['test_desc']
        self.logger.info('Test desc:'+test_desc)
        test_action=''
        if 'test_action' in item:
            test_action = item['test_action'].strip()
        data = ''
        if 'data' in item:
            data = str(item['data'])
            self.logger.info('传入值为： {} '.format(data))
        expectation =None
        optional = False  # 表示当前操作为不可选，必须通过后才可以执行下一步
        if 'expectation' in item:
            expectation = item['expectation']
            self.logger.info('本次操作期望值： {} '.format(expectation))
            pass
        if 'optional' in item:
            optional = item['optional']
            self.logger.info('本次操作是否必须： {} '.format(optional))
        result = True
        if 'test_control' not in item:  #当前操作不需要控件
            if 'send_key_event'==test_action :  # 模拟遥控按键
                num = 1
                if 'num' in item:
                    num = item['num']
                for i in range(num):
                    self.begin_time = int(time.time() * 1000)
                    if data.strip()=='back':
                        self.dash_page.send_key_event(data)
                    else:
                        self.dash_page.send_key_event(data)
                    self.begin_time=(self.begin_time +int(time.time() * 1000))/2
                    sleep(sleep_time/1000.0)
                    self.logger.info("执行按键："+data+"第"+str(i+1)+"次")
                sleep_time=0
                #self.begin_time=int(time.time()*1000) #记录按键时间
                pass
            elif test_action == 'image_search':  # 图片搜索
                dir, filename = os.path.split(self.path_yaml)
                data = os.path.join(dir, data) #待比较图片路径
                compare_begin_time=int(time.time())
                result=False
                img_path = os.path.join(os.path.dirname(dir),"TestResult/temp/test.jpg")
                while time.time()-compare_begin_time<15 and not result:
                    try:
                        #原生截图
                        self.dash_page.save_screenshot(img_path)
                        # result = ImageUtil.imageSearchInScreen(data)
                        # result = ImageUtil.imageSearch(img_path,data)
                    except Exception:
                        self.logger.info(traceback.print_exc())
                        sleep(3)
                self.logger.info('图像搜索结果：{}'.format(result))
            elif test_action == 'start_image_stream':
                dir, filename = os.path.split(self.path_yaml)
                global during,image_start_time
                image_start_time=int(time.time()*1000)
                during = item['data']
                path = self.ini.get_ini("test_case", "capture_image_path")
                self.tmp_path = path.encode("utf-8") + "/{}_".format(os.path.splitext(filename)[0]) + str(int(time.time()))
                if not os.path.exists(self.tmp_path):
                    os.makedirs(self.tmp_path)
                    # 创建目录后需要切换目录路径
                # os.chdir(self.tmp_path)
                # TODO 开启2个线程，一个截屏，一个图形比对
                capture_path = self.tmp_path + '/1.jpg'
                self.logger.info("视频流截图路径="+os.path.dirname(capture_path))
                print ("path="+os.path.dirname(capture_path))
                if during < 5000:
                    during = 5000
                global  captureThread
                # t = Thread(target=self.caputre_card.takePictureWithtimes, args=(capture_path, (2 * during / 1000)))
                self.logger.info('开始截图.......')
                captureThread=CaptureThread(self.caputre_card, capture_path, (during / 1000))
                captureThread.setDaemon(True)
                captureThread.start()
                result=True
                self.begin_time = int(time.time() * 1000)  #如果没有调用按键，直接将打开截图时间设为开始时间
                pass
            elif test_action == 'pixel_compare':  # 像素比较
                locations = item["data"]["locations"]
                rbgs = item["data"]["rgbs"]
                if "compare_type" in item["data"]:
                    compare_type= item["data"]["compare_type"] #1表示正向比较，0表示反向比较
                else:
                    compare_type=1
                try:
                    if compare_type==0:  #如果反向比较,默认是有一个不相等则表示通过
                        match_count = 1
                    else:
                        match_count = len(locations)
                    if "match_count" in item["data"]:
                        match_count=item["data"]["match_count"]
                    offset =10
                    if "offset" in item["data"]:
                        offset=item["data"]["offset"]
                        pass
                    spend_time,c_time,file=PixelCompareUtil(self.all_result_path[4],self.ini).pixel_compare(self.begin_time,"*.jpg",locations,rbgs,compare_type,match_count,offset,timeout=(during*2)/1000+3)
                    self.hit_image.append(file)
                    #print("spend_time={},c_time={}".format(spend_time,c_time))
                    if spend_time==-1:
                        result = False
                    else:
                        result=True
                except Exception as e:
                    self.logger.info('异常信息为='+str(e))
                    cost_time = cost_time+"<p>异常-2</p>"
                    spend_time=-2
                    return False
                pass
            elif test_action == 'screencap' or test_action == 'get_screencap' or test_action=='save_screencap':
                dir, filename = os.path.split(self.path_yaml)
                if not data:
                    self.logger.info('图片名称不能为空')
                    return False
                if test_action=='save_screencap':  #保存截图
                    pic_name = self.__save_screen_file(data)
                    self.pic_list.append(pic_name)
                else:
                    self.dash_page.save_screenshot(os.path.join(dir, data))
                pass
        #不管是否存在控件，以下操作都可以执行
        if test_action=='check_current_activity':
            self.logger.info('当前启动activity为:{}'.format(self.dash_page.current_activity()))
            if data in self.dash_page.current_activity()  or self.dash_page.current_activity() in data:
                result = True
            else:
                result = False
            pass
        elif test_action == 'outside_launch' or test_action=='open':
            self.begin_time = int(time.time() * 1000)
            self.logger.info('control action:{}'.format(test_action))
            try:
                self.adb.shell_multiple(data)
                sleep(sleep_time / 1000.0)
                sleep_time=0
                self.logger.info('outside_launch当前启动activity为:{}'.format(self.dash_page.current_activity()))
                if isinstance(expectation, str):
                    if expectation in self.dash_page.current_activity() or self.dash_page.current_activity() in expectation:
                        result = True
                    else:
                        result = False
                else:
                    result = True
            except Exception as e:
                result = False
                self.logger.info('启动发生异常{}'.format(traceback.format_exc()))
            #self.begin_time = int(time.time() * 1000)  #如果没有调用按键，直接将打开截图时间设为开始时间
            pass
        elif test_action=='switch_context':
            '''
              待测webview需要打开debug设置
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    this.appView.setWebContentsDebuggingEnabled(true);
                }
            '''
            contexts = self.driver.contexts
            print("contexts：%s" % contexts)
            try:
                if 'webview' in data:
                    self.driver.switch_to.context(contexts[-1])
                    pass
                else:
                    self.driver.switch_to.context(contexts[0])
                    pass
            except Exception as e:
                print("current_contexts：%s" % self.driver.current_context)
            sleep(5)
            contexts = self.driver.contexts
            print("contexts：%s" % contexts)
            self.logger.info('当前启动activity为:{}'.format(self.dash_page.current_activity()))
        elif test_action=='compute':
            expression= item["data"]
            operator = ['+', "-", "*", '/']
            tag_list=[]
            for i in operator:
                if i in expression:
                    tag_list=expression.split(i)
                    break
            if len(tag_list)<2:
                self.logger.info('传入表达式不正确')
                return
            try:
                if self.ini.get_ini("test", tag_list[0]):
                    tag1 = int(self.ini.get_ini("test", tag_list[0].strip()))
                else:
                    tag1=-1
                if self.ini.get_ini("test", tag_list[1]):
                    tag2 = int(self.ini.get_ini("test", tag_list[1].strip()))
                else:
                    tag2=-1
                self.logger.info(tag_list[0] + "={},"+tag_list[1]+"={}".format(str(tag1),str(tag2)))
                cost=0
                if "-" in expression:
                    cost = tag1 - tag2
                    pass
                elif "+" in expression:
                    cost = tag1 + tag2
                    pass
                elif "*" in expression:
                    cost = tag1 * tag2
                    pass
                elif tag2!=0 and "/" in expression:
                    cost = tag1 / tag2
                    pass
                cost_time =cost_time+"<p>"+expression+"=" +str(cost)+"</p>"
                spend_time=cost
                self.logger.info(expression+"="+ str(cost))
                print (expression+"="+ str(cost))
            except Exception as e:
                self.logger.error('请检查传入的表达式关键字正确,'+str(e))
                return False
            pass

        if "tag" in item:
            key = item["tag"].strip()
            if c_time!=-1:
                value = str(c_time)
            else:
                value = str(self.begin_time)  # 将结果转为毫秒后存放
            if spend_time < 0:
                if test_action == 'pixel_compare':
                    self.logger.info('比对超时')
                    cost_time = cost_time + "<p>像素比对超时-1</p>"
                    pass
                else:
                    self.ini.set_ini("test", key, value)
                    self.logger.info('保存记录当前key = {},value = {}'.format(key, value))
                    c_time = -1
            else:
                self.ini.set_ini("test", key, value)
                self.logger.info('保存记录当前key = {},value = {}'.format(key,value))
                c_time=-1
            pass
        else: #如果没有tag
            if test_action == 'pixel_compare':
                if spend_time <0:
                    self.logger.info('比对超时')
                    cost_time = cost_time + "<p>像素比对超时-1</p>"
                else:
                    self.logger.info(test_desc+'：{}'.format(str(spend_time)))
                    cost_time=cost_time+"<p>"+test_desc+":"+str(spend_time)+"</p>"
            pass

        if isinstance(expectation, bool):
            if expectation == result:
                result = True
            else:
                result = False
        elif isinstance(expectation,int):
            if spend_time<0 or spend_time>expectation:
                result=False
            else:
                result=True

        self.logger.info('operate result {}'.format(result))

        if not result:
            if optional:
                pic_name = self.__save_screen_file()
                self.pic_list.append(pic_name)
                self.logger.info('本次执行结果不影响后续操作')
            else:
                self.logger.info('本次执行结果必须正确，当前操作失败，任务退出')
                return False
        sleep(sleep_time / 1000.0)
        return True


    @log()
    def execute(self,cpu_list_info,mem_list_info):
        '''
        执行步骤
        1.开启测试
        2.生成测试报告
        :return:
        '''
        global cpu_list, mem_list
        cpu_list = cpu_list_info
        mem_list = mem_list_info

        self.data['start_time']=datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        # TimeUtil.sleep(3)
        result=self.__begin_parse()

        self.data['end_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        # TimeUtil.sleep(3)
        self.__generate_test_report()
        # FIXME 执行结果存数据库
        # zipfilename=zip_dir_to_one(self.all_result_path)
        # self.data['attachment_name'] = os.path.basename(zipfilename).split('.')[0]
        # self.data['status']=result
        # self.logger.info('保持结果到数据库')
        # self.save_result2db(zipfilename)
        self.logger.info('任务执行结束')
        pass

    @log()
    def __begin_parse(self):
        '''
        执行测试
        1.开启记录log
        2.执行测试
        3.记录执行结果
        4.存储执行结果
        5.截图
        :return:截图路径
        '''
        self.check_front_package()
        self.logger.info('read the yaml file')
        global_settings,items= get_yaml_info(self.path_yaml)

        if 'retry_times' in global_settings.keys():
            retry_time=global_settings.get('retry_times')
        else:
            retry_time=0
        if 'default_test_sleep' in global_settings.keys():
            self.default_test_sleep=global_settings.get('default_test_sleep')
        else:
            self.default_test_sleep=1000
        # self.__save_android_log()
        exec_result=self.__parse_yaml(global_settings,items)
        if isinstance(exec_result,bool):
            while retry_time>0 and not exec_result:
                self.logger.info('==========retry=========')
                time.sleep(3)
                self.check_front_package()
                exec_result = self.__parse_yaml(global_settings,items)
                retry_time=retry_time-1
        #序列化结果到本地文件
        with open(self.__save_error_status(),'w') as f:
            yaml.dump({'error_msg':exec_result,"cost_time":cost_time},f,allow_unicode = True)
            self.logger.info(str('result of the:%s' % exec_result))
        #TODO 判断流开关是否关闭
        remain_time = ( during - (int(time.time() * 1000) - image_start_time)) / 1000.0
        print "remain_time="+str(remain_time)
        if remain_time >= 0:
            self.logger.info('等待截图指令结束......')
            time.sleep(remain_time)
            self.logger.info('截图指令已结束......')
        time.sleep(1)
        pic_name=self.__save_screen_file()
        self.pic_list.append(pic_name)
        return exec_result

    @log()
    def check_front_package(self):
        '''
        检查当前设备的前端应用包名是否与指定launcher包名一致
        :return:
        '''
        # print("============================前端应用包名="+self.adb.get_front_package_name().strip())
        # print("============================被测应用包名=" +self.device_info['appPackage'])
        if self.dash_page.current_package() != self.device_info['appPackage']:
            self.logger.info(
                '前端应用包{}不正确，尝试进入首页{}'.format(self.adb.get_front_package_name().strip(), self.device_info['appPackage']))
            #self.dash_page.send_key_event(3)
            #time.sleep(3)
        pass

class Rectangle:
    """
    用于设置矩形，left,top,right,bottom
    """
    pass